package com.gitee.apanlh.util.algorithm.signature;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator.Builder;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.InvalidClaimException;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Verification;
import com.gitee.apanlh.exp.JwtException;
import com.gitee.apanlh.util.base.IteratorUtils;
import com.gitee.apanlh.util.base.MapUtils;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.check.CheckImport;
import com.gitee.apanlh.util.check.CheckLibrary;
import com.gitee.apanlh.util.valid.Assert;
import com.gitee.apanlh.util.valid.ValidParam;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.Map;

/**	
 * 	JWT签名
 *  #mark 重构
 * 	@author Pan
 */
public class JwtUtils {
	
	static {
		CheckImport.library(CheckLibrary.JWT);
	}
	
	/** 返回信息 */
	private static final String ILLEGAL_ACCESS_MSG = "非法访问";
	/** 载体 PAYLOAD */
	private static final String PAYLOAD = "data";
	/**	默认7天失效*/
	public static final long DEFAULT_EXPIRES_IN = 7L * (24L * 60L * 60L * 1000L);

	/**
	 * 	构造函数
	 *
	 * 	@author Pan
	 */
	private JwtUtils() {
		//	不允许外部实例
		super();
	}

	/**	
	 * 	创建HMAC256方式
	 * 
	 * 	@author Pan
	 * 	@param  signature  密钥
	 * 	@return Algorithm
	 */
	public static Algorithm createHmac256(String signature) {
		return Algorithm.HMAC256(signature);
	}
	
	/**	
	 * 	创建HMAC384方式
	 * 
	 * 	@author Pan
	 * 	@param  signature  密钥
	 * 	@return Algorithm
	 */
	public static Algorithm createHmac384(String signature) {
		return Algorithm.HMAC384(signature);
	}
	
	/**	
	 * 	创建HMAC512方式
	 * 
	 * 	@author Pan
	 * 	@param  signature  密钥
	 * 	@return Algorithm
	 */
	public static Algorithm createHmac512(String signature) {
		return Algorithm.HMAC512(signature);
	}
	
    /**	
     * 	生成JWT签名
     * 
     * 	@author Pan
     * 	@param  obj			对象
     * 	@param  algorithm	算法
	 * 	@return String
     */
    public static String sign(String obj, Algorithm algorithm) {
        return sign(obj, algorithm, DEFAULT_EXPIRES_IN);
    }

    /**
     * 	自定义生成JWT签名
     * 	
     * 	@author Pan
     * 	@param  obj			载体对象
     * 	@param  algorithm	签名方式
     * 	@param  expiresIn	有效期
     * 	@return String
     */
    public static String sign(String obj, Algorithm algorithm, Long expiresIn) {
    	Date iatTime = new Date(System.currentTimeMillis());
    	Date expTime = new Date(iatTime.getTime() + expiresIn);
    	
    	return JWT.create()
			//	默认参数
			/*.withIssuer(obj.toString())*/
			// 签发时间
			.withIssuedAt(iatTime) 
			// 过期时间
			.withExpiresAt(expTime)
			//自定义参数
			.withClaim(PAYLOAD, obj)
    	.sign(algorithm);
    }
    
    /**	
     * 	多签名载体信息
     * 	
     * 	@author Pan
     * 	@param  obj			对象
     * 	@param  algorithm	算法
     * 	@param  expiresIn	过期时间
     * 	@param  claims		Claims
     * 	@return	String
     */
    public static String sign(String obj, Algorithm algorithm, Long expiresIn, Map<String, String> claims) {
    	Date iatTime = new Date(System.currentTimeMillis());
    	Date expTime = new Date(iatTime.getTime() + expiresIn);
    	
    	Builder jwtBuilder = JWT.create()
			.withIssuedAt(iatTime) 
			.withExpiresAt(expTime)
			.withClaim(PAYLOAD, obj);
    	
    	if (!MapUtils.isEmpty(claims)) {
    		IteratorUtils.entrySet(claims, jwtBuilder::withClaim);
		}
    	return jwtBuilder.sign(algorithm);
    }
    
    /**
     * 	校验Token
     * 	<br>返回校验对象
     * 	
     * 	@author Pan
     * 	@param  token		token
     * 	@param  payload		载体
     * 	@param  algorithm 	算法类型
     * 	@return	JwtObject
     */
    public static JwtObject verifyJwt(String token, String payload, Algorithm algorithm) {
    	return verifyJwt(token, payload, algorithm, null);
    }
    
    /**	
     * 	校验Token
     * 	<br>多载体内容
     * 	<br>返回校验对象
     * 	
     * 	@author Pan
     * 	@param  token		token
     * 	@param  payload  	载体
     * 	@param  algorithm 	算法类型	 
     * 	@param  claims		多载体Map
     * 	@return	JwtObject
     */
    public static JwtObject verifyJwt(String token, String payload, Algorithm algorithm, Map<String, String> claims) {
    	JwtObject tokenAuth = new JwtObject();
    	tokenAuth.setToken(token);
    	try {
    		if (ValidParam.isEmpty(payload)) {
    			throw new InvalidClaimException("payload is null");
    		}
    		
    		Verification jwtVerification = JWT.require(algorithm).withClaim(PAYLOAD, payload);
    		
    		if (!MapUtils.isEmpty(claims)) {
    			IteratorUtils.entrySet(claims, jwtVerification::withClaim);
    		}
    		
    		JWTVerifier jwtBuilder = jwtVerification.build();
    		
    		jwtBuilder.verify(token);
    		tokenAuth.setSignCode(500100);
    		tokenAuth.setSignMsg("校验成功!");
    		tokenAuth.setVerifyFlag(true);
    	} catch (Exception e) {
    		try {
				throwsJwtException(tokenAuth, token, payload, e);
			} catch (Exception e1) {
				return tokenAuth;
			}
    	} 
    	return tokenAuth;
    }
    
    /**	
     * 	验证token是否过期
     * 	
     * 	@author Pan
     * 	@param 	jwt		jwt对象
     * 	@return	boolean
     */
    public static boolean isExpire(JwtObject jwt) {
		return jwt == null || 500104 == jwt.getSignCode();
    }
    
	/**	
	 * 	
	 * 	@author Pan
	 * 	@param  jwt		jwt对象
	 * 	@param  token	token值
	 * 	@param  payload	载体
	 * 	@param  e		异常
	 * 	@throws JwtException   校验不通过则抛出该异常
	 */
	private static void throwsJwtException(JwtObject jwt, String token, String payload, Exception e) throws JwtException{
    	if (e instanceof NullPointerException) {
    		jwt.setSignCode(500101);
    		jwt.setSignMsg(ILLEGAL_ACCESS_MSG);
    		jwt.setVerifyFlag(false);
    		throw new JwtException(StringUtils.format("空指针异常:伪造访问:{},信息为:{}", token, payload));
    	}
    	if (e instanceof SignatureVerificationException) {
    		jwt.setSignCode(500102);
    		jwt.setSignMsg(ILLEGAL_ACCESS_MSG);
    		jwt.setVerifyFlag(false);
    		throw new JwtException(StringUtils.format("签名信息不一致, 伪造访问:{},信息为:{}", token, payload));
    	}
    	if (e instanceof InvalidClaimException) {
    		jwt.setSignCode(500103);
    		jwt.setSignMsg(ILLEGAL_ACCESS_MSG);
    		jwt.setVerifyFlag(false);
    		throw new JwtException(StringUtils.format("载体信息不一致,伪造访问:{},信息为:{}", token, payload));
    	}
    	if (e instanceof TokenExpiredException) {
    		jwt.setSignCode(500104);
    		jwt.setSignMsg("token已失效");
    		jwt.setVerifyFlag(false);
    		throw new JwtException(StringUtils.format("token已失效,token为:{},信息为:{}", token, payload));
    	} 
    	if (e instanceof JWTDecodeException) {
    		jwt.setSignCode(500105);
    		jwt.setSignMsg(ILLEGAL_ACCESS_MSG);
    		jwt.setVerifyFlag(false);
    		throw new JwtException(StringUtils.format("token信息不完整伪造访问,token为:{},信息为:{}", token, payload));
    	}
    	if (e instanceof AlgorithmMismatchException) {
    		jwt.setSignCode(500106);
    		jwt.setSignMsg("伪造Header");
    		jwt.setVerifyFlag(false);
    		throw new JwtException(StringUtils.format("Header信息不完整伪造访问,token为:{},信息为:{}", token, payload), e);
    	}
		jwt.setSignCode(500200);
		jwt.setSignMsg("程序内部出现错误");
		jwt.setVerifyFlag(false);
		throw new JwtException(StringUtils.format("出现错误,token为:{},信息为:{}, 异常原因为:{}, 异常为:{}", token, payload, e.getMessage(), e));
    }

    /**	
     * 	获取签发时间
     * 	
     * 	@author Pan
     * 	@param 	jwt	完整的jwt
     * 	@return	LocalDateTime
     */
    public static LocalDateTime getIatTimeOfDateTime(String jwt) {
    	Assert.isNotEmpty(jwt);
		return LocalDateTime.ofInstant(JWT.decode(jwt).getIssuedAt().toInstant(), ZoneId.systemDefault());
    }
    
    /**	
     * 	获取过期时间
     * 	
     * 	@author Pan
     * 	@param 	jwt	完整的jwt字符串
     * 	@return	LocalDateTime
     */
    public static LocalDateTime getExpTimeOfDateTime(String jwt) {
    	Assert.isNotEmpty(jwt);
    	return LocalDateTime.ofInstant(JWT.decode(jwt).getExpiresAt().toInstant(), ZoneId.systemDefault());
    }
}	
